# coding:UTF-8 # 指定该文件编码为utf-8，编译中文能够正常显示
from typing import List  # 导入list类型提示
from pyecharts import options as opts  # 导入pyecharts中的options模块，配置对象
from pyecharts.globals import ThemeType  # 主题
from pyecharts.commons.utils import JsCode  # 导入jscode类，用于自定义javascript代码
from pyecharts.charts import Timeline,Grid,Bar,Map,Pie,Line  # 导入所需要的图表库
from sqlalchemy import create_engine,MetaData,Table,select
from sqlalchemy.orm import Session  # 导入sessionmaker，用于创建数据库会话
from sqlalchemy import select  # 导入select库，用于构建sql查询

# 1、数据处理操作

# 数据库连接配置
DATABASE_URL = 'mysql+pymysql://root:123456@localhost/employee' # 定义数据库连接字符串

# 创建数据库引擎
engine = create_engine(DATABASE_URL,echo=True) # 创建一个数据库引擎实例echo=True表示开启日志记录
session = Session() # 创建session对象，创建会话实例

# 反射表结构
metadata = MetaData()  # 创建一个元数据实例
gdp_data = Table('gba_data',metadata,autoload_with=engine)  # 反射gdp_data表

# 定义取得数据的函数

def get_gdp_data_from_db(year:str):
    with(engine.connect() as connection):  # 使用with语句打开数据库连接
        # 将year转换为整数
        year_int = int(year.strip('年'))  # 去掉年字并转换为整数
        query = select(gdp_data.c.province_name,gdp_data.c.gdp_value,gdp_data.c.growth_rate) .where(gdp_data.c.year == year_int)  # 构建sql查询，选定特定的年份的数据
        result = connection.execute(query).fetchall()  # 执行查询并获取所有结果
        for row in result:
            print(row)
        return [{"name":row[0],"value":[row[1],row[2],row[0]]} for row in result]  # 返回格式化后的数据，通过列表表达式 传递data当中的数据

# 生成时间列表
time_list = [str(d) + '年' for d in range(1993,2019)]  # 生成从1993到2018年的年份列表

# 全国GDP总量，通过读取数据之后，将所有的gdp_data通过pandas求均值
total_num =[
    3.4,4.5,5.8,6.8,7.6,8.3,8.8,9.9,10.9,12.1,
    14,16.8,19.9,23.3,28,33.3,36.5,43.7,52.1,
    57.7,63.4,68.4,72.3,78,84.7,91.5
]

# 设置最大值和最小值
maxNum = 97300
minNum = 30

def get_year_chart(year:str):
    # 从数据库中获取于year匹配的数据
    map_data = get_gdp_data_from_db(year)  # 获取指定年份的gdp数据
    min_data,max_data = (minNum,maxNum)  # 设置数据范围
    data_mask: List = []  # 初始化data_mask列表
    i = 0  # 初始化计数器
    for x in time_list:
        if x ==year:
            data_mask.append(total_num[i])  # 如果当前年份于year匹配，将对年龄的GDP总量添加到data_mark
        else:
            data_mask.append("")  # 否则添加空字符串
        i += 1  # 计数器加1


    # 2、创建地图图表
    map_chart = (
        Map()
        .add(
            series_name="", # 系列名称
            # 通过列表表达式（因为本身列表推导式就是简化版的循环）遍历每一个data中的name和value
            data_pair=[[x['name'],x['value'][0]] for x in map_data], # 提取省份名称和gdp的值
            zoom=1, # 地图缩放比例
            center=[199.5,34.5], # 地图中心点坐标
            is_map_symbol_show=False, # 不显示地图标记
            itemstyle_opts={
                "normal": {"areaColor":"#323c48","borderColor":"#404a59"},
                "emphasis": {
                    "label": {"show": True}, # 高亮时显示标签
                    "areaColor": "rgb(255,255,255,0.5)" # 高亮时的颜色
                }
            }
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(
                title=f"{year}全国分地区GDP情况（单位：亿） 数据来源：国家统计局", # 图标标题
                subtitle="", # 副标题
                pos_left="center", # 标题位置
                pos_top="top", # 顶部距离
                title_textstyle_opts=opts.TextStyleOpts(
                    font_size=25, # 字体大小
                    color="rgb(255,255,255,0.9)" # 字体颜色
                )
            ),
            tooltip_opts=opts.TooltipOpts(
                is_show=True,  # 显示提示框
                formatter=JsCode(
                    """function(params) {
                    if ('value' in params.data) {
                    return params.data.value[2] + ':' + params.data.value[0];
                    }
                    """
                ) # 自定义提示框
            ),
            visualmap_opts=opts.VisualMapOpts(
                is_calculable=True, # 是否显示可计算按钮
                dimension=0, # 维度
                pos_top="center",
                pos_left="30",
                range_text=['High','Low'],  # 范围文本
                textstyle_opts=opts.TextStyleOpts(color="#ddd"), # 文本样式
                min_=min_data, # 最小值
                max_=max_data  # 最大值
            )
        )
    )


    # 3、创建折线图
    line_chart = (
        Line()
        .add_xaxis(time_list)  # 添加x轴数据
        .add_yaxis("",total_num)  # 添加y轴数据
        .add_yaxis(
            "",
            data_mask,
            markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max")]), # 添加标记点
        )
        .set_series_opts(label_opts=opts.LabelOpts(is_show=False))  # 设置系列选项
        .set_global_opts(
            title_opts=opts.TitleOpts(
                title="全国GDP总量1993-2018(单位：万亿)",
                pos_left='72%',
                pos_top='5%'
            )
        )
    )


    # 4、创建柱状图
    bar_x_data = [x["name"] for x in map_data]  # x轴数据
    # 列表推导式，内部时两组键值对，分别是name对应的省市名称，value对应的GDP增长率和省市名称
    bar_y_data = [{"name": x["name"],"value": x["value"][0]} for x in map_data]  # y轴数据
    bar = (
        Bar()
        .add_xaxis(xaxis_data=bar_x_data)
        .add_yaxis(
            series_name="",  # 系列名称
            y_axis=bar_y_data,  # 添加y轴数据
            label_opts=opts.LabelOpts(
                is_show=True, # 显示标签
                position="right", # 标签位置
                formatter="{b} : {c}" # 标签格式
            )
        )
        .reversal_axis() # 将柱形图转置为水平柱形图
        .set_global_opts(
            xaxis_opts=opts.AxisOpts(
                max_=maxNum, # x轴最大值
                axislabel_opts=opts.LabelOpts(is_show=False) # x轴标签不显示
            ),
            yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(is_show=False)), # y轴标签不显示
            tooltip_opts=opts.TooltipOpts(is_show=False), # 不显示提示框
            visualmap_opts=opts.VisualMapOpts(
                is_calculable=True, # 是否显示可计算按钮
                dimension=0, # 维度
                pos_left="10",
                pos_top="top",
                range_text=["High","Low"], # 范围文本
                range_color=["lightskyblue","yellow","orangered"], # 范围颜色
                textstyle_opts=opts.TextStyleOpts(color="#cdd"), # 文本样式
                min_=min_data,
                max_=max_data
            )
        )
    )


    # 5、创建饼图
    pie_data = [[x["name"], x["value"][0]] for x in map_data]  # 饼图数据
    pie = (
        Pie()
        .add(
            series_name="", # 系列名称
            data_pair=pie_data, # 数据对
            radius=["15%","35%"], # 半径
            center=["80%","82%"], # 内半径 中心点 由于设置了内半径，所以此为圆环图
            itemstyle_opts=opts.ItemStyleOpts(
                border_width=1, # 边框宽度
                border_color="rgb(0,0,0,0.3)" # 边框颜色
            )
        )
        .set_global_opts(
            tooltip_opts=opts.TooltipOpts(is_show=True,formatter="{b} {d}%"), # 提示框内容
            legend_opts=opts.LegendOpts(is_show=False) # 不显示图例
        )
    )


    # 6、创建网格grid布局
    grid_chart = (
        Grid()
        .add(bar,grid_opts=opts.GridOpts(pos_left="10%",pos_right="45%",pos_top="50%",pos_bottom="5")) # 添加柱形图位置
        .add(line_chart,grid_opts=opts.GridOpts(pos_left="65%",pos_right="80",pos_top="10%",pos_bottom="50")) # 添加折线图位置
        .add(pie,grid_opts=opts.GridOpts(pos_left="45%",pos_right="60%")) # 添加饼图位置
        .add(map_chart,grid_opts=opts.GridOpts()) # 添加地图
    )
    return grid_chart




if __name__ == '__main__':
    timeline = Timeline(init_opts=opts.InitOpts(width='1600px',height='900px',theme=ThemeType.DARK)) # 创建时间轴对象
    for y in time_list:
        g = get_year_chart(year=y)  # 获取每年的图表
        timeline.add(g,time_point=str(y))  # 将图表添加到时间轴

    timeline.add_schema(
        orient="vertical", # 时间轴方向
        is_auto_play=True, # 这里设置自动播放
        is_inverse=True, # 逆序播放
        play_interval=500, # 播放间隔，如果觉得快，手动调整
     )

    timeline.render("中国GDP从1993年-2018年.html")



